package pers.vic.sso.client.filter;

import org.apache.commons.collections4.CollectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pers.vic.boot.base.tool.Tools;
import pers.vic.sso.client.listener.LogoutListener;
import pers.vic.sso.client.session.SessionMappingStorage;
import pers.vic.sso.common.constant.Oauth2Constant;
import pers.vic.sso.common.constant.SsoConstant;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * 描述:
 *  sso  client  Filter 的基类
 * @author Vic.xu
 * @date 2021-11-02 9:38
 */
public abstract class BaseClientFilter extends FilterParam implements Filter {

    protected Logger logger = LoggerFactory.getLogger(getClass());

    /** 排除URL */
    protected List<String> excludeUrls = new ArrayList<>();

    /**
     * 模糊匹配的url
     */
    private List<String> vagueUrlMatch = new ArrayList<>();

    /**
     * 是否进入 排除匹配: 必须配置了excludeUrls
     */
    private boolean willDoExclued;

    /**
     * 全量匹配的url
     */
    private List<String> fullUrlMatch = new ArrayList<>();

    private SessionMappingStorage sessionMappingStorage;

    public void setExcludeUrls(List<String> excludeUrls) {
        this.excludeUrls = excludeUrls;

    }

    public void addExcludeUrl(String excludeUrl) {
        this.excludeUrls.add(excludeUrl);


    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        willDoExclued = CollectionUtils.isNotEmpty(excludeUrls);
        if (!willDoExclued) {
            return;
        }
        Map<Boolean, List<String>> map = excludeUrls.stream().collect(Collectors.partitioningBy(u -> u.endsWith(SsoConstant.URL_FUZZY_MATCH)));
        fullUrlMatch = map.get(false);
        vagueUrlMatch = map.get(true);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        if (isExcludeUrl(Tools.getCurrentUrl((HttpServletRequest) request))) {
            chain.doFilter(request, response);
            return;
        }
        boolean accessAllowed = isAccessAllowed((HttpServletRequest) request, (HttpServletResponse) response);
        if (!accessAllowed) {
            return;
        }
        //some....
        chain.doFilter(request, response);

    }

    protected boolean isExcludeUrl(String url) {
        if (!willDoExclued) {
            return false;
        }

        //首先精确匹配
        if (fullUrlMatch.contains(url)) {
            return true;
        }

        //再进行模糊匹配
        for (String matchUrl : vagueUrlMatch) { // 再进行模糊匹配
            if (url.startsWith(matchUrl.replace(SsoConstant.URL_FUZZY_MATCH, ""))) {
                return true;
            }
        }
        return false;
    }

    ;

    @Override
    public void destroy() {
    }

    /**
     * 是否允许被登录
     * @param request
     * @param response
     * @return
     * @throws IOException
     */
    public abstract boolean isAccessAllowed(HttpServletRequest request, HttpServletResponse response)
            throws IOException;

    protected SessionMappingStorage getSessionMappingStorage() {
        if (sessionMappingStorage == null) {
            sessionMappingStorage = LogoutListener.getSessionMappingStorage();
        }
        return sessionMappingStorage;
    }

    protected void redirectLogin(HttpServletRequest request, HttpServletResponse response) throws IOException {
        if (isAjaxRequest(request)) {
            Tools.writeJson("未登录或已超时", response);
        } else {
            String loginUrl = new StringBuilder().append(getServerUrl()).append(SsoConstant.LOGIN_URL).append("?")
                    .append(Oauth2Constant.APP_ID).append("=").append(getAppId()).append("&")
                    .append(SsoConstant.REDIRECT_URI).append("=")
                    .append(URLEncoder.encode(getRedirectUrl(request), "utf-8")).toString();
            response.sendRedirect(loginUrl);
        }
    }

    protected boolean isAjaxRequest(HttpServletRequest request) {
        String requestedWith = request.getHeader("X-Requested-With");
        return requestedWith != null ? "XMLHttpRequest".equals(requestedWith) : false;
    }


    /**
     * 从sso server 重定向回来 应该去哪个页面
     * @param request
     * @return
     */
    abstract String getRedirectUrl(HttpServletRequest request);


}
